package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	r "math/rand"
	"strconv"
	"time"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
)

type EntityGo struct{}

type Entity struct {
	Table         string     `json:"Table,omitempty"` // lionel added  Table       string `json:"Table,omitempty"`
	Key           string     `json:"Key,omitempty"`
	EntityName    string     `json:"EntityName,omitempty"`
	Parent        []string   `json:"Parent,omitempty"`
	ParentSlice   [][]Entity `json:"ParentSlice,omitempty"` //不存数据库
	Son           []string   `json:"Son,omitempty"`
	DepartmentKey string     `json:"DepartmentKey,omitempty"`
	CreateTime    string     `json:"CreateTime,omitempty"`
	UpdateTime    string     `json:"UpdateTime,omitempty"`
	TxId          string     `json:"TxId,omitempty"` //不存数据库
	Number        int        `json:"Number,omitempty"`
	Desc          string     `json:"Desc,omitempty"`
}

type AddEventReturn struct {
	Key           string `json:"Key,omitempty"`
	DepartmentKey string `json:"DepartmentKey,omitempty"`
	Desc          string `json:"Desc,omitempty"`
	TxId          string `json:"TxId,omitempty"`
	CreateTime    string `json:"CreateTime,omitempty"`
}

type AddParam struct {
	Key    string `json:"Key,omitempty"`
	Number int    `json:"Number,omitempty"`
}

type MangoEntity struct {
	SelectorEntity Entity `json:"selector"`
}

func (s *EntityGo) Init(stub shim.ChaincodeStubInterface) peer.Response {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   init chaincode ,chaincode name : afc-data ,version :1.0")
	return shim.Success(nil)
}
func (s *EntityGo) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

	function, args := stub.GetFunctionAndParameters()

	if function == "EntityAdd" {
		return s.EntityAdd(stub, args)
	} else if function == "EntityGet" {
		return s.EntityGet(stub, args)
	} else if function == "EntityUpdate" {
		return s.EntityUpdate(stub, args)
	} else if function == "EntityGetSon" {
		return s.EntityGetSon(stub, args)
	} else if function == "EntityQuery" {
		return s.EntityQuery(stub, args)
	}
	return shim.Error("Invalid Smart Contract function name.")
}

func main() {
	err := shim.Start(new(EntityGo))
	if err != nil {
		fmt.Printf("Error creating new  Contract: %s", err)
	}
}

/*
 * 更新
 */
func (s *EntityGo) EntityUpdate(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   function is : DepartmentUpdate", args)
	//获取详情
	key := args[0]
	entity, err := s.EntityDetail(stub, key)
	if err != nil {
		return shim.Error("Detail func error:" + err.Error())
	}
	//根据条件替换详情
	var o Entity
	err = json.Unmarshal([]byte(args[1]), &o)
	if err != nil {
		return shim.Error("Incorrect parameters,please check your parameters")
	}
	if len(o.Parent) > 0 || o.Number != 0 {
		return shim.Error("Incorrect parameters,you can`t update parent or number")
	}
	if o.EntityName != "" {
		entity.EntityName = o.EntityName
	}
	if o.DepartmentKey != "" {
		entity.DepartmentKey = o.DepartmentKey
	}

	entity.UpdateTime = time.Now().Format("2006-01-02 15:04:05")
	dbData, _ := json.Marshal(entity)
	stub.PutState(key, dbData)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   EntityUpdate exec success")

	// 如果son节点不为空

	return shim.Success(nil)
}

/*
 * 这个query用于没有传Key参数时，显示所有的值
 */
func (s *EntityGo) EntityQuery(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   function is : EntityQuery", args)
	var o Entity
	err := json.Unmarshal([]byte(args[0]), &o)
	if err != nil {
		return shim.Error(err.Error())
	}

	queryObject := MangoEntity{
		SelectorEntity: o,
	}

	queryString, err := json.Marshal(queryObject)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   query string is : ", string(queryString))
	resultsIterator, err := stub.GetQueryResult(string(queryString))

	defer resultsIterator.Close()
	var results []Entity
	for resultsIterator.HasNext() {
		var result Entity
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// lionel added 20180816
		ch := make(chan string, 1)
		go func(c chan string, str string) { c <- str }(ch, string(queryResponse.GetValue()))
		strData := <-ch
		err = json.Unmarshal([]byte(strData), &result) // lionel modified 20180816
		// end
		//err = json.Unmarshal(queryResponse.GetValue(), &result)
		if err != nil {
			return shim.Error(err.Error())
		}
		result.Key = queryResponse.GetKey()
		results = append(results, result)
	}

	resp, _ := json.Marshal(results)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   EntityQuery exec success")
	return shim.Success(resp)
}

// end

/*
 * 查看详情
 */
func (s *EntityGo) EntityGet(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	key := args[0]
	/*//往下查询找到最新的 TODO 忽略往下找
	entity, _ := s.EntitySon(stub, key)
	fmt.Println("EntitySon:", entity)*/
	entity, err := s.EntityDetail(stub, key)
	if err != nil {
		return shim.Error("Detail func error:" + err.Error())
	}
	fmt.Println("Enitiy detail:", entity)
	//历史查询
	resultsIterator, _ := stub.GetHistoryForKey(entity.Key)
	defer resultsIterator.Close()
	var results []Entity
	for resultsIterator.HasNext() {
		var result Entity
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		err = json.Unmarshal(queryResponse.Value, &result)
		if err != nil {
			return shim.Error(err.Error())
		}
		result.Key = key
		result.TxId = queryResponse.TxId
		results = append(results, result)
	}
	if len(results) == 0 {
		return shim.Success([]byte("[]"))
	}
	//往上查询
	results[len(results)-1].ParentSlice, _ = s.EntityParent(stub, results[len(results)-1].Parent, 0)
	resp, _ := json.Marshal(results)
	return shim.Success(resp)
}

/*
 * TODO 单个往下找的方法
 */

func (s *EntityGo) EntityGetSon(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   function is : EntityGetSon", args)
	key := args[0]
	entity, err := s.EntityDetail(stub, key)
	if err != nil {
		return shim.Error("Detail func error:" + err.Error())
	}
	var son []Entity
	for _, v := range entity.Son {
		entity, err := s.EntityDetail(stub, v)
		if err != nil {
			return shim.Error("Detail func error:" + err.Error())
		}
		son = append(son, entity)
	}
	dbData, _ := json.Marshal(son)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   son is : ", string(dbData))
	return shim.Success(dbData)
}

/*
 * entity创建
 */
func (s *EntityGo) EntityAdd(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   function is : DepartmentAdd", args)
	var o Entity
	err := json.Unmarshal([]byte(args[0]), &o)
	if err != nil {
		return shim.Error("Incorrect parameters,please check your parameters")
	}
	if o.EntityName == "" || o.DepartmentKey == "" {
		return shim.Error("parameters have null string,please check your parameters")
	}

	o.CreateTime = time.Now().Format("2006-01-02 15:04:05")
	o.UpdateTime = time.Now().Format("2006-01-02 15:04:05")

	rand.Seed(time.Now().UnixNano())
	randNum := rand.Int63n(100000000)
	key := strconv.FormatInt(time.Now().Unix()+randNum, 10)
	o.Key = key
	/*
	  添加这个entity时
	  判断这个entity是否为原材料
	  如果不是，则修改其父节点得状态
	*/
	if len(args) == 2 {
		var p []AddParam
		err := json.Unmarshal([]byte(args[1]), &p)
		if err != nil {
			return shim.Error("Incorrect parameters,please check your parameters")
		}
		o, err = s.EntityMerge(stub, o, p, key)
		if err != nil {
			return shim.Error("EntityMerge err" + err.Error())
		}
	}
	dbData, _ := json.Marshal(o)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  newEntity is :", string(dbData))

	stub.PutState(key, dbData)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "   afc_data_add exec success")
	//tosend := "Event send data is here!"
	var addEventReturn AddEventReturn
	addEventReturn.CreateTime = o.CreateTime
	addEventReturn.DepartmentKey = o.DepartmentKey
	addEventReturn.Key = o.Key
	addEventReturn.Desc = o.Desc
	addEventReturn.TxId = stub.GetTxID()
	returnData, _ := json.Marshal(addEventReturn)
	err = stub.SetEvent("addEntity", returnData)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success([]byte(key))
}

func (s *EntityGo) EntityMerge(stub shim.ChaincodeStubInterface, newEntity Entity, args []AddParam, key string) (Entity, error) {
	//遍历父节点
	for _, needEntity := range args {
		//查询其父节点
		var rawEntity Entity
		bytes, err := stub.GetState(needEntity.Key)
		if err != nil {
			return newEntity, err
		}
		err = json.Unmarshal(bytes, &rawEntity)
		if err != nil {
			return newEntity, err
		}
		//判断需要得父节点number数与原父节点的number数
		if needEntity.Number < rawEntity.Number {
			/*如果所需父节点number数小于原父节点的number数
			则对原父节点进行拆分,生成2个基于原父节点的2个新entity
			这两个新entity的父节点为原父节点*/
			workEntityKey := strconv.FormatInt(time.Now().Unix()+r.Int63n(100000000), 10)
			notWorkEntityKey := strconv.FormatInt(time.Now().Unix()+r.Int63n(100000000), 10)

			workEntity := CreateNewEntity(rawEntity.EntityName, newEntity.DepartmentKey, needEntity.Number, rawEntity.Key, workEntityKey)
			//workEntity的son为newEntity
			workEntity.Son = append(workEntity.Son, newEntity.Key)
			notWorkEntity := CreateNewEntity(rawEntity.EntityName, newEntity.DepartmentKey, rawEntity.Number-needEntity.Number, rawEntity.Key, notWorkEntityKey)
			r.Seed(time.Now().UnixNano())

			workEntityData, _ := json.Marshal(workEntity)
			notWorkEntityData, _ := json.Marshal(notWorkEntity)
			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  key is", workEntityKey, " workEntity is :", string(workEntityData))

			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  key is", notWorkEntityKey, " notWorkEntity is :", string(notWorkEntityData))

			stub.PutState(workEntityKey, workEntityData)
			stub.PutState(notWorkEntityKey, notWorkEntityData)
			//修改原父节点的son为这两个子节点
			rawEntity.Son = append(rawEntity.Son, workEntityKey)
			rawEntity.Son = append(rawEntity.Son, notWorkEntityKey)
			rawEntity.UpdateTime = time.Now().Format("2006-01-02 15:04:05")
			rawEntityData, _ := json.Marshal(rawEntity)
			stub.PutState(rawEntity.Key, rawEntityData)
			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  rawEntity is :", string(rawEntityData))
			//修改newEntity的父节点为workEntity
			newEntity.Parent = append(newEntity.Parent, workEntityKey)
		} else if needEntity.Number == rawEntity.Number {
			/*如果所需父节点的number等于原父节点的number数
			  则不需要对原父节点进行拆分，直接变更原父节点的number数和son*/
			rawEntity.Son = append(rawEntity.Son, newEntity.Key)
			rawEntityData, _ := json.Marshal(rawEntity)
			stub.PutState(rawEntity.Key, rawEntityData)
			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  rawEntity is :", string(rawEntityData))
			//修改newEntity的父节点为rawEntity
			newEntity.Parent = append(newEntity.Parent, rawEntity.Key)
		} else if needEntity.Number > rawEntity.Number {
			return newEntity, errors.New("not enough number of newEntity")
		}
	}
	return newEntity, nil
}

/*-----------------------------------------------------功能 方法-----------------------------------------------------*/
func CreateNewEntity(entityName string, departmentKey string, number int, parent string, key string) Entity {
	var e Entity
	e.Key = key
	e.DepartmentKey = departmentKey
	e.EntityName = entityName
	e.Number = number
	e.Parent = append(e.Parent, parent)
	e.CreateTime = time.Now().Format("2006-01-02 15:04:05")
	e.UpdateTime = time.Now().Format("2006-01-02 15:04:05")
	return e
}

func (s *EntityGo) EntityDetail(stub shim.ChaincodeStubInterface, key string) (Entity, error) {
	var en Entity
	Bytes, err := stub.GetState(key) // 这个地方传进来的是个key，如果用Table名字的话，这个地方没有参数。
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "  EntityDetail is :", string(Bytes))
	if err != nil {
		return en, err
	}
	err = json.Unmarshal(Bytes, &en) //反序列化
	if err != nil {
		return en, err
	}
	return en, nil
}

/*
 * 找到Entity的所有父类
 */
func (s *EntityGo) EntityParent(stub shim.ChaincodeStubInterface, args []string, i int) ([][]Entity, error) {
	var rEn [][]Entity
	if i > 200 {
		return rEn, nil
	}
	for _, v := range args {
		resultsIterator, _ := stub.GetHistoryForKey(v)
		var results []Entity
		for resultsIterator.HasNext() {
			var result Entity
			queryResponse, err := resultsIterator.Next()
			if err != nil {
				return rEn, err
			}
			err = json.Unmarshal(queryResponse.Value, &result)
			if err != nil {
				return rEn, err
			}
			result.Key = v
			result.TxId = queryResponse.TxId
			results = append(results, result)
		}
		if len(results) > 0 {
			if len(results[0].Parent) > 0 {
				i++
				results[0].ParentSlice, _ = s.EntityParent(stub, results[0].Parent, i)
			}
		}
		resultsIterator.Close()
		rEn = append(rEn, results)
	}
	return rEn, nil
}
